<?php
namespace Service\VenderClient\WayBillExpressCompanyApi;

use Service\Code\Normal;
use Service\BaseService;
use Service\ExtraService\TaskWorker;
use Service\HttpServer\Http;

/**
 * 顺丰api
 * @author: hj
 */
class SfCompanyApiClient extends BaseService {
    //生产
    const API_URL_PROD = 'https://bspgw.sf-express.com/std/service';
    //沙箱
    const API_URL_DEV = 'https://sfapi-sbox.sf-express.com/std/service';
    public    $apiList = [
        'createOrder'  => 'EXP_RECE_CREATE_ORDER',//下单
        'printWaybill' => 'COM_RECE_CLOUD_PRINT_WAYBILLS',//打印面单
    ];
    protected $config  = [];

    public function __construct(array $apiBody = []) {
        $this->config = $apiBody;
    }

    /**
     * 顺丰下单申报
     * @param array $declareInfo
     * @return array
     */
    public function logisticDeclare(array $declareInfo = []) {
        $receiveInfo     = $declareInfo['receiveInfo'];
        $senderInfo      = $declareInfo['senderInfo'];
        $orderGoods      = $declareInfo['orderGoods'];
        $order           = $declareInfo['order'];
        $goods           = $declareInfo['goods'];
        $e_company       = $declareInfo['e_company'];
        $receiverAddress = $receiveInfo['ReceiverProvince'] . $receiveInfo['ReceiverCity'] . $receiveInfo['ReceiverCountry'] . $receiveInfo['ReceiverAddress'];
        if ($order['express_num']) {
            return Normal::returnCode(Normal::FAIL, '顺丰不支持重推运单，请联系顺丰处理');
        }
        $weight    = 0;
        $goodsList = [];
        foreach ($orderGoods as $item) {
            $itemWeight  = round(($goods[$item['goods_id']]['weight']) / 1000, 3);
            $goodsList[] = [
                'name'       => $goods[$item['goods_id']]['name'],//货物名称，如果需要生成电子运单，则为必填。
                'count'      => $item['num'],//货物数量 验货服务、跨境件报关需要填写
                'unit'       => $goods[$item['goods_id']]['unit_name'], //货物单位，如：个、台、本，跨境件报关需要填写。
                'weight'     => (float)$itemWeight, //订单货物单位重量，包含子母件，单位千克，精确到小数点后3位跨境件报关需要填写。
                'amount'     => (float)$item['price'],
                'currency'   => 'CNY',//人民币
                'sourceArea' => 'CHN',//原产地国别， 跨境件报关需要填写
            ];
            $weight      = bcadd((string)$weight, (string)$itemWeight, 3);
        }
        $contactInfoList = [
            //寄件方信息
            [
                'contactType' => 1,//地址类型：1，寄件方信息2，到件方信息 3，委托人信息
                'contact'     => $senderInfo['SenderName'],//联系人–与公司必输一个
                'tel'         => $senderInfo['SenderPhone'],//联系电话–与联系方手机必输一个
                'mobile'      => $senderInfo['SenderPhone'],//联系方手机–与联系方手机必输一个
                'country'     => 'CN',//国家或地区 2位代码 参照附录国家代码附件–国内传CN
                'province'    => $senderInfo['SenderProvince'],//省份
                'city'        => $senderInfo['SenderCity'],//市
                'county'      => $senderInfo['SenderCountry'],//区
                'address'     => $senderInfo['SenderAddress'],//详细地址
            ],
            //收件方信息
            [
                'contactType' => 2,//地址类型：1，寄件方信息2，到件方信息 3，委托人信息
                'contact'     => $receiveInfo['ReceiverName'],//联系电话–与联系方手机必输一个
                'tel'         => $receiveInfo['ReceiverPhone'],//联系电话–与联系方手机必输一个
                'mobile'      => $receiveInfo['ReceiverPhone'],//联系电话–与联系方手机必输一个
                'country'     => 'CN',//联系电话–与联系方手机必输一个
                'province'    => $receiveInfo['ReceiverProvince'],//省份
                'city'        => $receiveInfo['ReceiverCity'],//市
                'county'      => $receiveInfo['ReceiverCountry'],//区
                'address'     => $receiverAddress,//详细地址
            ]
        ];
        $data            = [
            'language'           => 'zh-CN',//目前支持以下值zh-CN表示中文简体，zh-TW或zh-HK或zh-MO表示中文繁体，en表示英文
            'orderId'            => $order['sn'],//客户订单号
            'cargoDetails'       => $goodsList,//拖寄物信息
            'contactInfoList'    => $contactInfoList,//收寄双方信息
            'monthlyCard'        => $this->config['monthlyCard'],//顺丰月结卡号–请与业务方确认月结卡号信息。
            // 'payMethod'          => 1,//付款方式，支持以下值：1:寄方付 2:收方付 3:第三方付
            'expressTypeId'      => 2,//快件产品类别，支持附录《快件产品类别表》的产品编码值，仅可使用与顺丰销售约定的快件产品。电商退货产品传固定值236
            // 'isDocall'           => 0,//是否通过手持终端通知顺丰收派员上门收件，支持以下值：1：要求 0：不要求 (默认0) *电商退货固定传1
            'isReturnRoutelabel' => 1,//是否返回路由标签：默认0，1：返回路由标签，0：不返回
            'isReturnQRCode'     => 1,//是否返回返回二维码链接
        ];
        $reqParam        = [
            'method' => 'createOrder',
            'data'   => $data
        ];
        $result          = $this->execute($reqParam);
        if ($result['code'] != Normal::SUC) {
            return $result;
        }
        $apiResultData = json_decode($result['data']['apiResultData'], true);
        if (isset($apiResultData['errorCode']) && $apiResultData['errorCode'] != 'S0000') {
            // return Normal::returnCode(Normal::FAIL, "请求成功", $resHttp);
            $message = $apiResultData['errorMsg'] ?? 'SF下单失败1';
            return Normal::returnCode(Normal::FAIL, $message);
        }
        if (isset($apiResultData['success']) && $apiResultData['success'] === false) {
            // return Normal::returnCode(Normal::FAIL, "请求成功", $resHttp);
            $message = $apiResultData['errorMsg'] ?? 'SF下单失败2';
            return Normal::returnCode(Normal::FAIL, $message);
        }
        if (!isset($apiResultData['msgData']) || empty($apiResultData['msgData'])) {
            $message = $apiResultData['errorMsg'] ?? 'SF下单失败3';
            return Normal::returnCode(Normal::FAIL, $message);
        }
        $msgData        = $apiResultData['msgData'];
        $routeLabelInfo = reset($msgData['routeLabelInfo']);
        $routeLabelData = $routeLabelInfo['routeLabelData'];

        return Normal::returnCode(Normal::SUC, "运单号申报成功", [
            'waybillNo'    => $routeLabelData['waybillNo'] ?? '',//运单号
            'bigWord'      => $routeLabelData['destRouteLabel'] ?? '',//大字/三段码
            'packagePlace' => $routeLabelData['destTeamCode'] ?? '',//集包地
            'originData'   => json_encode($result['data'], JSON_UNESCAPED_UNICODE),//返回的原始数据result字符串
        ]);
    }

    /**
     * CURL请求
     * @param $params
     * @return array
     */
    public function execute($param = []) {
        if (empty($param['data'])) {
            return Normal::returnCode(Normal::FAIL, "SF请求数据不能为空");
        }
        if (!isset($this->apiList[$param['method']])) {
            return Normal::returnCode(Normal::FAIL, "SF请求方法不能为空");
        }
        if (!isset($this->config['appEnv']) || empty($this->config['appEnv'])) {
            return Normal::returnCode(Normal::FAIL, "运行环境未配置");
        }
        $method    = $param['method'];
        $timeStp   = time();
        $pushData  = [
            'partnerID'   => $this->config['partnerID'],//合作伙伴编码（即顾客编码）
            'requestID'   => $this->uuid(),//请求唯一号UUID
            'serviceCode' => $this->apiList[$method],//接口服务代码（EXP_RECE_CREATE_ORDER）
            'timestamp'   => $timeStp,//调用接口时间戳
            'msgDigest'   => $this->sign($param['data'], $timeStp),//数字签名
            'msgData'     => json_encode($param['data'], JSON_UNESCAPED_UNICODE),//数字签名
        ];
        $parameter = [
            [
                'tag'    => 'SF_' . $method,
                'url'    => $this->config['appEnv'] == 'dev' ? self::API_URL_DEV : self::API_URL_PROD,
                'data'   => $pushData,
                'method' => 'post',
                'format' => 'x-www-form-urlencoded',
                'header' => [
                    'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'
                ],
            ],
        ];
        $resTask   = TaskWorker::instance()->addTask($parameter);
        $resTask   = current($resTask);
        $res       = $resTask['response']['result'];
        $resHttp   = @json_decode($res, true);
        if (isset($resHttp['apiResultCode']) && $resHttp['apiResultCode'] == 'A1000') {
            return Normal::returnCode(Normal::SUC, "请求成功", $resHttp);
        }
        $message = $resHttp['apiErrorMsg'] ?? '请求失败';
        return Normal::returnCode(Normal::FAIL, $message);
    }

    /**
     * 返回毫秒级时间戳
     * @return int
     */
    public static function getMillisecond() {
        list($s1, $s2) = explode(' ', microtime());
        return (int) sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
    }

    /**
     * 加签
     * 顾客编码   ZBWYFVM6TBTD
     * 顺丰校验码  CkItPppkUybFXqxMmbvHzmid2Is4UgkF
     * 顺丰月结卡号  0520307242
     */
    public function sign($data, $timeStp) {
        $str1 = json_encode($data, JSON_UNESCAPED_UNICODE);
        $str2 = $str1 . $timeStp . $this->config['checkWord'];
        return base64_encode(md5((urlencode($str2)), TRUE));
    }

    /**
     * 获取uuid
     */
    public function uuid() {
        $chars = md5(uniqid((string)(mt_rand()), true));
        return substr($chars, 0, 8) . '-'
            . substr($chars, 8, 4) . '-'
            . substr($chars, 12, 4) . '-'
            . substr($chars, 16, 4) . '-'
            . substr($chars, 20, 12);
    }
}